#include "MapType.h"

#include "PlaceholderType.h"

#include "Reflection/NativeFunction.h"
#include "Reflection/FunctionList.h"
#include "Reflection/ForwardLinkedListType.h"

#include "Containers/ReflectableMap.h"
#include "GarbageCollectable.h"

#include "Framework/Subsystem.h"

CString CMapType::PlaceholderNameKey = U"Key";
CString CMapType::PlaceholderNameValue = U"Value";

CMapType::CMapType()
    : CType(
        STypeID(CType::GetEngineCoreNamespace()+U"::TNativeMap",true)
        ,ETypeFlags::Generic | ETypeFlags::Map
    )
{
    GenericInfo= new CGenericInfo();

    auto KeyType = new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderNameKey,true)
        ,ETypeFlags::None
        ,0
        ,this);
    auto ValueType = new CPlaceholderType(
        STypeID(GetFullName()+U"::"+PlaceholderNameValue ,true)
        ,ETypeFlags::None
        ,1
        ,this);

    GenericInfo->PlaceholderTypes.Add(KeyType);
    GenericInfo->PlaceholderTypes.Add(ValueType);

    Functions= new CFunctionList(this);
    CNativeFunction* AddFunction=nullptr;
    {
        //add function
        AddFunction = new CNativeFunction(
            Functions,
            U"Add", 
            CGetType<void>()(),
            {KeyType,ValueType},
            {}
            );
  
    }
    {
        //remove function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions,
            U"Remove", 
            CGetType<void>()(),
            {KeyType},
            {}
        );

    }
    {
        //Clear function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions
            ,U"Clear"
            , CGetType<void>()()
            ,{}
            ,{}
            );

    }

    {
        //contains function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions
            ,U"Contains"
            , CGetType<bool>()()
            ,{KeyType}
            ,{}
            );

    }

    CNativeFunction* FindFunction=nullptr;
    {
        //find function
        FindFunction = new CNativeFunction(
            Functions
            ,U"Find"
            ,ValueType
            ,{KeyType}
            ,{}
            );

    }
 
    {
        //create a indexer in functions
        CFunctionList::SIndexer Indexer;
        Indexer.Getter=FindFunction;
        Indexer.Setter=AddFunction;
        Indexer.ParameterTypes.Add(KeyType /*index type*/  );
    
        Functions->Indexers.Add(Indexer);
    }
}


CMapType *CMapType::GetStaticType()
{
    static CMapType *StaticType = new CMapType();
    return StaticType;
}

CString CMapType::CreateTypeName(const CString &KeyType, const CString &ValueType)
{
    return STypeID::CreateTemplateTypeName(
        GetStaticType()->GetFullName()
        ,{KeyType,ValueType}
        ,{ PlaceholderNameKey, PlaceholderNameValue }
        );
}

STypeID CMapType::CreateID(const SDecoratedType &KeyType, const SDecoratedType &ValueType)
{
    return STypeID(
        GetStaticType()->ID
        ,{KeyType.BasicType->ID,ValueType.BasicType->ID}
        ,{ PlaceholderNameKey, PlaceholderNameValue }
        );
}

static uint8_t RegisterTypesCaller=(CMapType::GetStaticType(),0);

class CMapInstance : public ITypeInstance
{
protected:
    CMapGeneralized* Type;
public:
    CMapInstance(CMapGeneralized* InType)
        :Type(InType)
    {
    }

    void InitInstance(void* Memory) override
    {
        new (Memory) CReflectableMap(Type->GetForwardForwardLinkedListType());
    }

    void CloneInstance(const void* Instance, void* Memory) override
    {
        new (Memory) CReflectableMap(Type->GetForwardForwardLinkedListType(), *(CReflectableMap*)Instance);
    }

    void EraseInstance(void* InValue) override
    {
        ((CReflectableMap*)InValue)->Clear(Type->GetForwardForwardLinkedListType());
        ((CReflectableMap*)InValue)->~CReflectableMap();
    }

    void* CreateInstance() override
    {
        void* Memory = malloc(Type->GetSize());
        InitInstance(Memory);
        return Memory;
    }

    void ResetAllocated(void* InMemory) override
    {
        ((CReflectableMap*)InMemory)->Clear(Type->GetForwardForwardLinkedListType());
    }

    uint32_t GetSize() override
    {
        return sizeof(CReflectableMap);
    }

    CString ToDebugString(const void* InInstance) const override
    {
        return Type->GetFullName();
    }

    uint64_t GetInstanceHash(const void* InInstance) const override
    {
        return reinterpret_cast<CReflectableMap const*>(InInstance)->GetHashCode(Type->GetForwardForwardLinkedListType());
    }

    bool IsInstanceEqual(const void* Instance0, const void* Instance1) const override
    {
        return reinterpret_cast<CReflectableMap const*>(Instance0)->IsEqual(Type->GetForwardForwardLinkedListType(), *reinterpret_cast<CReflectableMap const*>(Instance1));
    }

};

class CMapGC : public IGarbageCollectable
{
protected:
    CMapGeneralized* Type;
public:
    CMapGC(CMapGeneralized* InType)
        :Type(InType)
    {
    }

    void CollectReferencedObjects(void* InInstance, TForwardForwardLinkedList<RObject*>& OutReferencedObjects) override
    {
        CReflectableMap* Map = (CReflectableMap*)InInstance;
        CType* KeyType = Type->GetKeyType().BasicType;
        if(KeyType->GarbageCollectable)
        {
            auto Keys = Map->NativeGetKeys(Type->GetForwardForwardLinkedListType());
            for (auto Key :Keys)
            {
                KeyType->GarbageCollectable->CollectReferencedObjects(Key, OutReferencedObjects);
            }
        }

        CType* ValueType = Type->GetValueType().BasicType;
        if(ValueType->GarbageCollectable)
        {
            auto Values = Map->NativeGetValues(Type->GetForwardForwardLinkedListType());
            for (auto Value :Values)
            {
                ValueType->GarbageCollectable->CollectReferencedObjects(Value, OutReferencedObjects);
            }
        }
    }
};


CMapGeneralized::CMapGeneralized(
    const SDecoratedType& InKeyType
    ,const SDecoratedType& InValueType
    ,int64_t ValueOffset
    )
    : CType(
        CMapType::CreateID(InKeyType,InValueType)
        ,ETypeFlags::Map | ETypeFlags::Generalized
         |(InKeyType.BasicType->CanUseInScript()&&InValueType.BasicType->CanUseInScript()?ETypeFlags::None:ETypeFlags::CantUseInScript)
        )
{
    Instancable = new CMapInstance(this);

    GeneralizedInfo = new CGeneralizedInfo();
    GeneralizedInfo->TemplateType = CMapType::GetStaticType();
    GeneralizedInfo->TypeArguments.Add(InKeyType.BasicType);
    GeneralizedInfo->TypeArguments.Add(InValueType.BasicType);

    GeneralizedInfo->TemplateType->GenericInfo->GeneralizedTypes.Add(this);
    reinterpret_cast<CGenericInfo*>(GeneralizedInfo->TemplateType->GenericInfo)
        ->GenericMap.Add(
            TVector<CType*>({InKeyType.BasicType,InValueType.BasicType})
            ,this);


    PairType= CPairGeneralized::FindOrAddType(InKeyType,InValueType,ValueOffset);
    if(PairType->GarbageCollectable)
    {
        GarbageCollectable = new CMapGC(this);
    }
    
    ForwardLinkedListType = CForwardLinkedListGeneralized::FindOrAddType(PairType);

    Functions= new CFunctionList(this);

    CNativeFunction* AddFunction=nullptr;
    {
        //add function
        AddFunction = new CNativeFunction(
            Functions
            ,U"Add"
            ,CGetType<void>()()
            ,{InKeyType,InValueType}
            ,{}
            );
        // AddFunction->AddParameter(InKeyType);
        // AddFunction->AddParameter(InValueType);
        AddFunction->SetCppBody([this](const TVector<CAny>& InArgs)->CAny
        {
            CReflectableMap* Map = *(CReflectableMap**)InArgs[0].GetPointer();
            auto& Key = InArgs[1];
            auto& Value = InArgs[2];
            Map->Add(ForwardLinkedListType,Key.GetPointer(),Value.GetPointer());
            return CAny();
        });

    }

    {
        //remove function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions
            ,U"Remove"
            , CGetType<void>()()
            ,{InKeyType}
            ,{}
            );
        //NativeFunction->AddParameter(InKeyType);
        NativeFunction->SetCppBody([this](const TVector<CAny>& InArgs)->CAny
        {
            CReflectableMap* Map = *(CReflectableMap**)InArgs[0].GetPointer();
            auto& Key = InArgs[1];
            Map->Remove(ForwardLinkedListType,Key.GetPointer());
            return CAny();
        });

    }

    {
        //Clear function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions
            ,U"Clear"
            , CGetType<void>()()
            ,{}
            ,{}
            );
        NativeFunction->SetCppBody([this](const TVector<CAny>& InArgs)->CAny
        {
            CReflectableMap* Map = *(CReflectableMap**)InArgs[0].GetPointer();
            Map->Clear(ForwardLinkedListType);
            return CAny();
        });

    }

    {
        //contains function
        CNativeFunction* NativeFunction = new CNativeFunction(
            Functions
            ,U"Contains"
            , CGetType<bool>()()
            ,{InKeyType}
            ,{}
            );

        //NativeFunction->AddParameter(InKeyType);
        NativeFunction->SetCppBody([this](const TVector<CAny>& InArgs)->CAny
        {
            CReflectableMap* Map = *(CReflectableMap**)InArgs[0].GetPointer();
            auto& Key = InArgs[1];
            return Map->Contains(ForwardLinkedListType,Key.GetPointer());
        });
    }

    CNativeFunction* FindFunction=nullptr;
    {
        //find function
        FindFunction = new CNativeFunction(
            Functions
            ,U"Find"
            , InValueType
            ,{InKeyType}
            ,{}
            );
        //FindFunction->AddParameter(InKeyType);
        FindFunction->SetCppBody([this](const TVector<CAny>& InArgs)->CAny
        {
            CReflectableMap* Map = *(CReflectableMap**)InArgs[0].GetPointer();
            void* Key = InArgs[1].GetPointer();
            return CAny(GetValueType().BasicType
                ,GetValueType().BasicType->CloneInstance(Map->FindValue(ForwardLinkedListType,Key))
                );
        });
    }

    {
        //create a indexer in functions
        CFunctionList::SIndexer Indexer;
        Indexer.Getter=FindFunction;
        Indexer.Setter=AddFunction;
        Indexer.ParameterTypes.Add(InKeyType /*index type*/  );
    
        Functions->Indexers.Add(Indexer);
    }

}

CPairGeneralized *CMapGeneralized::GetPairType()
{
    return PairType;
}

SDecoratedType CMapGeneralized::GetKeyType() const
{
    return GeneralizedInfo->TypeArguments[0];
}

SDecoratedType CMapGeneralized::GetValueType() const
{
    return GeneralizedInfo->TypeArguments[1];
}

CForwardLinkedListGeneralized *CMapGeneralized::GetForwardForwardLinkedListType() const
{
    return ForwardLinkedListType;
}

// CString CMapGeneralized::CreateTypeName(CType *Key, CType *Value)
// {
//     return CType::GetEngineCoreNamespace()+U"::TNativeMap<" + Key->GetFullName() + U"," + Value->GetFullName() + U">";
// }